#ifndef PROCESSING_H_
#define PROCESSING_H_

#include "frequent_subgraphs_mining.h"

namespace masugo
{


template<typename ParsingType>
class processing
{
public:
	typedef typename ParsingType::FGraph FGraph;
	typedef frequent_subgraphs_mining<FGraph> fsgm;

	static void process(const std::string& file, const double support, const bool only_subtrees, int support_steps, const double support_step_size)
	{
		const std::deque<typename ParsingType::FGraph> graphs=ParsingType::read_graphs(file.c_str());
		if(graphs.size()<2) { throw std::invalid_argument("Not enough graphs, should be at least two"); }

		bool valid=true;
		for(int i=0;i<=support_steps && valid;i++)
		{
			const double used_support=(support-(i*support_step_size));
			const int min_support=static_cast<std::size_t>(static_cast<double>(graphs.size())*used_support);
			if(min_support>=2)
			{
				std::cout << utilities::ParametricString("support_threshold={}\n").arg(used_support).str();
				std::cout << utilities::ParametricString("input_graphs_count={}\n").arg(graphs.size()).str();
				std::cout << utilities::ParametricString("minimal_support_count={}\n").arg(min_support).str();

				process(graphs, static_cast<std::size_t>(min_support), only_subtrees,  utilities::ParametricString("{F}_sup_{S}").arg("{F}", file).arg("{S}", used_support).str());
			}
			else { valid=false; }
		}
	}

private:
	static void process(const std::deque<FGraph>& graphs, const std::size_t min_support, const bool only_subtrees, const std::string& output_prefix)
	{
		typename fsgm::MiningResult mining_result=fsgm::mine_frequent_subgraphs(graphs, min_support, only_subtrees);

		std::cout << utilities::ParametricString("mining_time={}\n").arg(mining_result.running_time/1000.0).str();

		process_frequent_subtrees(mining_result.frequent_subtrees, graphs, output_prefix);

		if(!only_subtrees)
		{
			process_maximal_frequent_subgraphs(mining_result.maximal_frequent_subgraphs, graphs, output_prefix);
		}

		std::cout << std::endl;
	}

	static void process_frequent_subtrees(const typename fsgm::FrequentSubtreeResultMap& frequent_subtrees, const std::deque<FGraph>& graphs, const std::string& output_prefix)
	{
		int maximal_frequent_subtrees_count=0;
		std::size_t max_vertices_count=0;

		if(!frequent_subtrees.empty())
		{
			std::multimap<int, std::string> output_ordering;
			for(typename fsgm::FrequentSubtreeResultMap::const_iterator it=frequent_subtrees.begin();it!=frequent_subtrees.end();it++)
			{
				if(it->second.successors_count==0)
				{
					std::size_t vertices_count=it->second.ftree.vertices_count();
					max_vertices_count=std::max(vertices_count, max_vertices_count);
					output_ordering.insert(std::make_pair(0-static_cast<int>(vertices_count), it->first));
				}
			}
			maximal_frequent_subtrees_count=output_ordering.size();

			std::ofstream output((output_prefix+"_max_freq_subtrees").c_str(), std::ios::out);
			int num=0;
			for(std::multimap<int, std::string>::const_iterator it=output_ordering.begin();it!=output_ordering.end();it++)
			{
				const typename fsgm::FrequentSubtreeResult& fsr=frequent_subtrees.find(it->second)->second;
				output << "//tree\n";
				output << "////id=" << ++num << "\n";
				output << "////vertices_count=" << fsr.ftree.vertices_count() << "\n";
				output << "////occurences_count=" << fsr.occurences.size() << "\n";
				print_occurences(fsr, graphs, output);
				print_graph_in_graphviz_format(fsr.ftree, "T", true, output);
				output << "\n";
			}
		}

		std::cout << utilities::ParametricString("frequent_subtrees_count={}\n").arg(frequent_subtrees.size()).str();
		std::cout << utilities::ParametricString("max_freq_subtrees_count={}\n").arg(maximal_frequent_subtrees_count).str();
		std::cout << utilities::ParametricString("max_freq_subtrees_max_vertices={}\n").arg(max_vertices_count).str();
	}

	static void process_maximal_frequent_subgraphs(const typename fsgm::MaximalFrequentSubgraphResultList& maximal_frequent_subgraphs, const std::deque<FGraph>& graphs, const std::string& output_prefix)
	{
		std::size_t max_vertices_count=0;
		std::size_t max_edges_count=0;

		if(!maximal_frequent_subgraphs.empty())
		{
			typedef std::multimap< std::pair<int, int>, std::size_t > OutputOrdering;
			OutputOrdering output_ordering;
			for(std::size_t i=0;i<maximal_frequent_subgraphs.size();i++)
			{
				std::size_t vertices_count=maximal_frequent_subgraphs[i].fgraph.vertices_count();
				max_vertices_count=std::max(vertices_count, max_vertices_count);
				std::size_t edges_count=maximal_frequent_subgraphs[i].fgraph.edges_count();
				max_edges_count=std::max(edges_count, max_edges_count);
				output_ordering.insert(std::make_pair(std::make_pair(0-static_cast<int>(vertices_count), 0-static_cast<int>(edges_count)), i));
			}

			std::ofstream output((output_prefix+"_max_freq_subgraphs").c_str(), std::ios::out);
			int num=0;
			for(OutputOrdering::const_iterator it=output_ordering.begin();it!=output_ordering.end();it++)
			{
				const typename fsgm::MaximalFrequentSubgraphResult& mfsr=maximal_frequent_subgraphs[it->second];
				output << "//graph\n";
				output << "////id=" << ++num << "\n";
				output << "////vertices_count=" << mfsr.fgraph.vertices_count() << "\n";
				output << "////edges_count=" << mfsr.fgraph.edges_count() << "\n";
				output << "////occurences_count=" << mfsr.occurences.size() << "\n";
				print_occurences(mfsr, graphs, output);
				print_graph_in_graphviz_format(mfsr.fgraph, "G", true, output);
				output << "\n";
			}
		}

		std::cout << utilities::ParametricString("max_freq_subgraphs_count={}\n").arg(maximal_frequent_subgraphs.size()).str();
		std::cout << utilities::ParametricString("max_freq_subgraphs_max_vertices={}\n").arg(max_vertices_count).str();
		std::cout << utilities::ParametricString("max_freq_subgraphs_max_edges={}\n").arg(max_edges_count).str();
	}

	template<typename T>
	static void print_occurences(const T& obj, const std::deque<FGraph>& graphs, std::ostream& output)
	{
		output << "//occurences: "; for(std::size_t i=0;i<obj.occurences.size();i++)
		{
			output << graphs[obj.occurences[i]].name() << (i+1<obj.occurences.size() ? " " : "\n");
		}
	}

	template<typename GraphType, typename Name>
	static void print_graph_in_graphviz_format(const GraphType& graph, const Name& graph_name, const bool print_edge_labels, std::ostream& output)
	{
		output << utilities::ParametricString("graph {Name}\n{\n").arg("{Name}", graph_name).str();
		for(typename GraphType::Vertices::const_iterator it=graph.vertices().begin();it!=graph.vertices().end();it++)
		{
			output << utilities::ParametricString("node_{ID} [label=\"{Name}\"]\n").arg("{ID}", it->first).arg("{Name}", it->second.value).str();
		}
		typename GraphType::Edges edges=graph.collect_edges();
		for(std::size_t i=0;i<edges.size();i++)
		{
			if(print_edge_labels)
			{
				output << utilities::ParametricString("node_{A} -- node_{B} [label=\"{L}\"]\n").arg("{A}", edges[i].second.first).arg("{B}", edges[i].second.second).arg("{L}", edges[i].first).str();
			}
			else
			{
				output << utilities::ParametricString("node_{A} -- node_{B}\n").arg("{A}", edges[i].second.first).arg("{B}", edges[i].second.second).str();
			}
		}
		output << "}\n";
	}
};

}



#endif /* PROCESSING_H_ */
